---
title: Merge document ranges into one PDF
summary: Merge multiple document ranges into a single PDF, optionally applying a post-merge PDF/A pass.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-09-17
library: PdfAPI.SyncFusionCore
reference_type: package
operation_id: OP-SFC-012
trigger: Caller request (SyncfusionPdfService constructs new PdfMerger and calls Merge)
---

# Merge document ranges into one PDF

Merges a set of document ranges into a single PDF. The merge itself is always performed without conformance; PDF/A is applied only as an optional, best-effort post-merge pass whose failure is logged rather than surfaced.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-SFC-012 |
| **Trigger** | Caller request — `SyncfusionPdfService` constructs `new PdfMerger(environmentService, logService)` and calls `Merge` (`PdfAPI\Services\Syncfusion\SyncfusionPdfService.cs:23`) |
| **Inputs** | `MergeRequest` (`DocumentRanges[]`, `Options`, `SourceSystemId`), optional `trackingId` |
| **Outputs** | `PdfResult` with `SourceConversionType.Cache`, `Status = Succeeded`, `PdfProcessorType = Syncfusion`, `Elapsed`, `ImageId`, `TrackingId` |
| **Primary components** | `PdfMerger.Merge` → private `MergeDocumentRanges` |
| **Entry point** | `PdfApi.SyncFusionCore\Merge\PdfMerger.cs` |

## Steps

1. `Merge` creates a discarded `Guid` and a placeholder `PdfResult`, starts a `Stopwatch`, and calls
   `MergeDocumentRanges` inside a `try`/`finally`; the `finally` stops the stopwatch and stamps `Elapsed`.
   - On failure: there is no `catch`, so any exception in `MergeDocumentRanges` propagates to the
     caller ([BR-SFC-028](../business-rules.md#br-sfc-028-extraction-and-merge-failures-propagate)).
2. Create the output `PdfDocument` with conformance `None` — the merge itself is always
   non-conformant, regardless of `Options.IsPdfa` ([BR-SFC-029](../business-rules.md#br-sfc-029-merge-performed-at-no-conformance)).
3. For each `DocumentRange`: call `range.Document.Load()` (reads `FilePath` from disk when `File` is
   null, `PdfAPI.Core`), then use the whole file when the range is a single original range, or call [Extract page ranges from a PDF](extract-page-ranges-from-pdf.md) when the range has more than one `PageRange` or its first range `IsPartial`. Collect the byte arrays ([BR-SFC-030](../business-rules.md#br-sfc-030-whole-file-or-extracted-subset-per-range)).

   > **Origin**: Step 3's extraction is implemented in [`Extract page ranges from a PDF`](extract-page-ranges-from-pdf.md) (`PdfAPI.SyncFusionCore`) and executed on behalf of this operation.
4. Call `Syncfusion.Pdf.PdfDocument.Merge(outputDoc, docs.ToArray())`, save the result to a
   `MemoryStream` and capture the bytes.
5. When `Options.IsPdfa`: reload the merged bytes, set `Conformance = Pdf_A1B`, save, and — only
   when the produced byte array is non-empty — replace the result and set `request.Options.OutputFormat = PdfFormat.PdfA` ([BR-SFC-031](../business-rules.md#br-sfc-031-pdf%2Fa-replaces-plain-pdf-only-on-success)).
   - If the PDF/A pass throws: build a warning message (appending `[TrackingId: n]` when a tracking
     id was supplied), construct an `ApplicationExceptionDetail` via [Build an application exception detail](build-application-exception-detail.md) (`PdfHelper.GetApplicationDetail(_envService, "Merge", msg, ex)`), and call `_logService.LogException(detail)`. Processing continues and the non-conformant PDF is returned ([BR-SFC-032](../business-rules.md#br-sfc-032-pdf%2Fa-merge-failure-logged-as-warning)).

     > **Origin**: The exception-detail construction is implemented in [`Build an application exception detail`](build-application-exception-detail.md) (`PdfAPI.SyncFusionCore`) and executed on behalf of this operation.
6. Wrap the bytes with `Document.Load(result, "pdf")`, set `Status = Succeeded` unconditionally,
   `PdfProcessorType = Syncfusion`, and `OutputFormat = PdfFormat.Pdf` — the local `outputFormat` variable is never updated by the PDF/A branch, so a successful PDF/A merge still reports `OutputFormat = Pdf` on the result while `request.Options.OutputFormat` reports `PdfA` ([BR-SFC-033](../business-rules.md#br-sfc-033-merge-result-always-reported-succeeded)).
7. Parse `request.SourceSystemId` into `ImageId` with `long.TryParse`, set
   `TrackingId = trackingId ?? 0`, and return the result ([BR-SFC-009](../business-rules.md#br-sfc-009-source-system-id-parsed-to-numeric-image-id)).

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
	Start(["Merge called with<br>MergeRequest, trackingId"]) --> CreateOut["Create output PdfDocument<br>with conformance None"]
	CreateOut --> ForEach{"More DocumentRanges<br>to process?"}
	ForEach -->|Yes| LoadRange[range.Document.Load]
	LoadRange --> RangeType{"Single original<br>range?"}
	RangeType -->|Yes| WholeFile[Use whole file]
	RangeType -->|No| ExtractSub["Extract page ranges<br>from a PDF"]
	WholeFile --> ForEach
	ExtractSub --> ForEach
	ForEach -->|No| MergeDocs["PdfDocument.Merge<br>all collected docs"]
	MergeDocs --> Save[Save merged bytes]
	Save --> PdfaCheck{"Options.IsPdfa?"}
	PdfaCheck -->|No| Wrap["Wrap with Document.Load,<br>Status = Succeeded"]
	PdfaCheck -->|Yes| Reload["Reload, set Conformance<br>= Pdf_A1B, save"]
	Reload --> ReloadOk{"Reload throws?"}
	ReloadOk -->|No| ReplaceCheck{"Produced bytes<br>non-empty?"}
	ReplaceCheck -->|Yes| PromoteFormat["Replace result,<br>promote OutputFormat<br>to PdfA"]
	ReplaceCheck -->|No| Wrap
	PromoteFormat --> Wrap
	ReloadOk -->|Yes| LogWarn["Log warning via<br>ApplicationExceptionDetail,<br>keep non-conformant PDF"]
	LogWarn --> Wrap
	Wrap --> ParseId["Parse SourceSystemId,<br>set TrackingId"]
	ParseId --> Result([Return PdfResult])
```

## Failure and edge-case behavior

- A failure before or during the initial merge (steps 1–4) propagates to the caller — there is no
  surrounding `catch`.
- A failure only in the post-merge PDF/A pass (step 5) is caught, logged, and does not fail the
  operation: the plain PDF is returned and `Status` is still `Succeeded`.
- A merge result is always reported `Succeeded`, even when the PDF/A upgrade failed and only the
  non-conformant PDF was returned.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

## Sources
- `PdfApi.SyncFusionCore\Merge\PdfMerger.cs`
- `PdfApi.SyncFusionCore\Extract\PdfExtractor.cs`
- `PdfApi.SyncFusionCore\PdfHelper.cs`
- `PdfAPI\Services\Syncfusion\SyncfusionPdfService.cs`
